<?php

namespace App\Http\Controllers\AgentAdmin;

use App\Enums\ProductStatus;
use App\Enums\ProductType;
use App\Enums\TokenLockType;
use App\Enums\TokenStatus;
use App\Http\Controllers\Controller;
use App\Models\AgentUser;
use App\Models\BlindBoxItem;
use App\Models\BlindBoxProduct;
use App\Models\Product;
use App\Models\ProductConfig;
use App\Models\ProductToken;
use App\Services\ProductService;
use Illuminate\Support\Arr;
use Vinlon\Laravel\LayAdmin\Exceptions\AdminException;

class BlindBoxController extends Controller
{
    /** @var ProductService */
    private $productService;

    /**
     * BlindBoxController constructor.
     */
    public function __construct(ProductService $productService)
    {
        $this->productService = $productService;
    }

    /** 创建肓盒 */
    public function createBlindBox()
    {
        request()->validate([
            'store_id' => 'required',
            'name' => 'required',
            'issuer' => 'nullable',
            'display_price' => 'required',
            'buy_limit' => 'required',
            'start_time' => 'nullable',
            'end_time' => 'nullable',
            'intro' => 'nullable',
            'description' => 'required',
            'logo_url' => 'required',
            'product_ids' => 'required',
            'show_left_count' => 'required | boolean',
        ]);

        // 检查商品库存
        $productIds = explode(',', request()->product_ids);
        $productQuantities = [];
        foreach ($productIds as $productId) {
            $productQuantities[$productId] = Arr::get($productQuantities, $productId, 0) + 1;
        }
        $products = $this->findProducts(array_keys($productQuantities), request()->store_id);

        // 检查商品及库存
        foreach ($productQuantities as $productId => $quantity) {
            /** @var Product $product */
            $product = Arr::get($products, $productId);
            if (!$product) {
                throw new AdminException("商品ID【{$productId}】未找到");
            }
            if ($product->available_tokens_count < $quantity) {
                throw new AdminException("商品【{$product->name}】可用数量为{$product->available_tokens_count}");
            }
        }

        \DB::transaction(function () use ($productIds, $productQuantities, $products) {
            /** @var AgentUser $user */
            $user = auth()->user();
            // 创建商品（商品类型为BlindBox)
            $product = new Product();
            $product->agent_id = $user->agent_id;
            $product->store_id = request()->store_id;
            $product->type = ProductType::BLIND_BOX();
            $product->issuer = request()->issuer ?: '';
            $product->asset_id = 0;
            $product->public_hash = '';
            $product->contract_address = '';
            $product->publisher_account = '';
            $product->name = request()->name;
            $product->logo_url = request()->logo_url;
            $product->price = toFen(request()->display_price);
            $product->intro = request()->intro;
            $product->description = request()->description;
            $totalCount = array_sum($productQuantities);
            $product->issue_count = $totalCount;
            $product->left_count = $totalCount;
            $product->start_time = request()->start_time;
            $product->end_time = request()->end_time;
            $product->buy_limit = request()->buy_limit;
            $product->status = ProductStatus::INIT();
            $product->save();

            //记录contract_address
            $product->contract_address = 'blind_box_' . $product->id;
            $product->save();

            //保存是否显示实时库存的配置
            ProductConfig::set($product->contract_address, ProductConfig::SHOW_LEFT_COUNT, request()->show_left_count);

            $productTokens = [];
            foreach ($productQuantities as $productId => $quantity) {
                /** @var Product $insideProduct */
                $insideProduct = Arr::get($products, $productId);

                // 锁定商品的token
                $tokenIds = ProductToken::query()
                    ->select(['id'])
                    ->where('product_id', $productId)
                    ->where('status', TokenStatus::INIT)
                    ->limit($quantity)
                    ->get()->map(function (ProductToken $token) {
                        return $token->id;
                    })->toArray();

                $productTokens[$productId] = $tokenIds;

                ProductToken::query()->whereIn('id', $tokenIds)
                    ->update([
                        'status' => TokenStatus::LOCKED(),
                        'lock_type' => TokenLockType::BLIND_BOX,
                        'lock_refer_id' => $product->id,
                    ]);

                // 创建肓盒商品记录
                $blindBoxProduct = new BlindBoxProduct();
                $blindBoxProduct->blind_box_id = $product->id;
                $blindBoxProduct->inside_product_id = $productId;
                $blindBoxProduct->quantity = $quantity;
                $blindBoxProduct->save();
            }

            // 生成肓盒队列
            $batchValues = [];
            foreach ($productIds as $index => $productId) {
                $tokenIds = Arr::get($productTokens, $productId);
                $tokenId = array_pop($tokenIds);
                $productTokens[$productId] = $tokenIds;
                $batchValues = [];
                $batchValues[] = [
                    'created_at' => now(),
                    'updated_at' => now(),
                    'blind_box_id' => $product->id,
                    'product_id' => $productId,
                    'product_token_id' => $tokenId,
                    'sequence' => $index + 1,
                    'status' => TokenStatus::INIT(),
                ];
                BlindBoxItem::insert($batchValues);
            }
            //BlindBoxItem::insert($batchValues);
        });
    }

    /** 修改商品基础信息 */
    public function editBasicInfo($id)
    {
        request()->validate([
            'name' => 'required',
            'issuer' => 'nullable',
            'display_price' => 'required',
            'buy_limit' => 'required',
            'sequence' => 'nullable | integer',
            'start_time' => 'nullable',
            'end_time' => 'nullable',
            'logo_url' => 'required',
            'intro' => 'nullable',
            'description' => 'required',
            'show_left_count' => 'required | boolean',
        ]);

        $product = $this->findBlindBoxOrFail($id);
        $product->contract_address = 'blind_box_' . $id;
        $product->name = request()->name;
        $product->issuer = request()->issuer ?: '';
        $product->price = toFen(request()->display_price);
        $product->buy_limit = request()->buy_limit;
        $product->sequence = request()->sequence ?: 0;
        $product->start_time = request()->start_time;
        $product->end_time = request()->end_time;
        $product->logo_url = request()->logo_url;
        $product->intro = request()->intro;
        $product->description = request()->description;
        $product->save();

        //保存是否显示实时库存的配置
        ProductConfig::set($product->contract_address, ProductConfig::SHOW_LEFT_COUNT, request()->show_left_count);
    }

    /** 查询可以放入肓盒的商品 */
    public function listAvailableProducts()
    {
        request()->validate([
            'store_id' => 'required',
        ]);
        // 非在售状态的发行商品
        return $this->findProducts(null, request()->store_id);
    }

    /** 查询肓盒内的商品列表 */
    public function listProductsInBlindBox($id)
    {
        return BlindBoxProduct::query()
            ->with(['product'])
            ->where('blind_box_id', $id)
            ->orderBy('quantity')
            ->get();
    }

    /** 查询出奖队列 */
    public function listBlindBoxItems($id)
    {
        $query = BlindBoxItem::query()
            ->with(['product', 'product_token'])
            ->where('blind_box_id', $id)
            ->orderBy('sequence');

        return paginate_result($query);
    }

    /** 肓盒上架 */
    public function online($id)
    {
        $bbox = $this->findBlindBoxOrFail($id);
        if (!$bbox->status->in([ProductStatus::INIT, ProductStatus::PAUSE])) {
            throw new AdminException('只有未发行和暂停发行的肓盒可以执行上架操作');
        }
        $bbox->status = ProductStatus::ON_SALE();
        $bbox->online_time = now();
        $bbox->save();
    }

    /** 肓盒下架 */
    public function offline($id)
    {
        $bbox = $this->findBlindBoxOrFail($id);
        if (!$bbox->status->is(ProductStatus::ON_SALE())) {
            throw new AdminException('只有发行中的肓盒可以执行下架操作');
        }
        $bbox->status = ProductStatus::PAUSE();
        $bbox->save();
    }

    /** 肓盒停售 */
    public function stop($id)
    {
        $bbox = $this->findBlindBoxOrFail($id);
        if (!$bbox->status->in([ProductStatus::INIT, ProductStatus::PAUSE])) {
            throw new AdminException('只有未发行和暂停发行的肓盒可以停售');
        }

        \DB::transaction(function () use ($bbox) {
            $bbox->status = ProductStatus::STOP();
            $bbox->save();
            $productTokenIds = BlindBoxItem::query()
                ->where('blind_box_id', $bbox->id)
                ->where('status', TokenStatus::INIT)
                ->get()->map(function (BlindBoxItem $item) {
                    return $item->product_token_id;
                });
            // 释放锁定的存证
            ProductToken::query()->whereIn('id', $productTokenIds)
                ->update([
                    'status' => TokenStatus::INIT,
                    'lock_type' => null,
                    'lock_refer_id' => null,
                ]);
        });
    }

    /** 生成肓盒"出奖"队列 */
    public function generateProductQueue()
    {
        request()->validate([
            'store_id' => 'required',
            'product_id' => 'required | array',
            'quantity' => 'required | array',
        ]);
        $productIds = request()->product_id;
        $quantities = request()->quantity;
        if (array_sum($quantities) > 10000) {
            throw new AdminException('总发行数量不能超过10000');
        }
        if (count(array_unique($productIds)) !== count($productIds)) {
            throw new AdminException('不能选择重复的商品');
        }
        $products = $this->findProducts($productIds, request()->store_id);
        $productQuantities = [];
        foreach ($productIds as $index => $productId) {
            $quantity = Arr::get($quantities, $index);

            /** @var Product $product */
            $product = Arr::get($products, $productId);
            if (!$product) {
                throw new AdminException("商品ID【{$productId}】未找到");
            }
            if ($product->available_tokens_count < $quantity) {
                throw new AdminException("商品【{$product->name}】可用数量为{$product->available_tokens_count}");
            }
            $productQuantities[$productId] = $quantity;
        }

        $queue = [];
        // 取出最小的商品数量$min， 进行$min轮的随机, 保证稀有商品均匀分配
        while (true) {
            $qtyValues = array_filter(array_values($productQuantities));
            if (empty($qtyValues)) {
                break;
            }
            $min = min($qtyValues);
            $pool = [];
            foreach ($productQuantities as $productId => &$quantity) {
                $num = floor($quantity / $min);
                $quantity -= $num;
                for ($j = 1; $j <= $num; ++$j) {
                    $pool[] = $productId;
                }
            }
            $queue = array_merge($queue, Arr::shuffle($pool));
        }

        $productQueue = [];
        foreach ($queue as $index => $productId) {
            /** @var Product $product */
            $product = Arr::get($products, $productId);
            $productQueue[] = [
                'sequence' => $index + 1,
                'name' => $product->name,
            ];
        }

        return [
            'product_ids' => $queue,
            'product_queue' => $productQueue,
        ];
    }

    /**
     * @param $id
     *
     * @throws AdminException
     *
     * @return Product
     */
    private function findBlindBoxOrFail($id)
    {
        /** @var AgentUser $user */
        $user = auth()->user();
        $product = $this->productService->getAgentProductQuery($user->agent_id)
            ->where('type', ProductType::BLIND_BOX)
            ->find($id);
        if (!$product) {
            throw new AdminException('肓盒ID未找到');
        }

        return $product;
    }

    private function findProducts($productIds = null, $storeId)
    {
        /** @var AgentUser $user */
        $user = auth()->user();

        return $this->productService->getAgentProductQuery($user->agent_id)
            ->withCount(['store', 'available_tokens'])
            ->where('store_id', $storeId)
            ->where('status', '!=', ProductStatus::ON_SALE)
            ->where('type', ProductType::ISSUE)
            ->when($productIds, function ($q) use ($productIds) {
                return $q->whereIn('id', $productIds);
            })
            ->get()
            ->mapWithKeys(function (Product $product) {
                return [$product->id => $product];
            });
    }
}
